/*
 * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
 *
 * SPDX-License-Identifier: MIT
 */
/*
 * @Hardwares: M5Core + Module COMX NBIoT
 * @Platform Version: Arduino M5Stack Board Manager v2.1.3
 * @Dependent Library:
 * M5Stack@^0.4.6: https://github.com/m5stack/M5Stack
 */

#include <M5Stack.h>
#include <stdint.h>
#include <vector>
#include "TFTTerminal.h"

// Description: Check signal strength,Register to the network
// Note: 5 and 13 of the DIP switch are set to ON.

TFT_eSprite Disbuff      = TFT_eSprite(&M5.Lcd);
TFT_eSprite TerminalBuff = TFT_eSprite(&M5.Lcd);
TFTTerminal terminal(&TerminalBuff);
TaskHandle_t xhandle_lte_event = NULL;
SemaphoreHandle_t command_list_samap;

typedef enum {
    kQUERY_MO = 0,
    KTEST_MO,
    kASSIGN_MO,
    kACTION_MO,
    kQUERY_MT,
    kTEST_MT,
    kASSIGN_MT,
    kACTION_MT,
    kINFORM
} LTEMsg_t;

typedef enum {
    kErrorSendTimeOUT = 0xe1,
    kErrorReError     = 0xe2,
    kErroeSendError   = 0xe3,
    kSendReady        = 0,
    kSending          = 1,
    kWaitforMsg       = 2,
    kWaitforRead      = 3,
    kReOK
} LTEState_t;

struct ATCommand {
    uint8_t command_type;
    String str_command;
    uint16_t send_max_number;
    uint16_t max_time;
    uint8_t state;
    String read_str;
    uint16_t _send_count;
    uint16_t _send_time_count;

} user;

using namespace std;
vector<ATCommand> serial_at;
String zmmi_str;
void LTEModuleTask(void* arg)
{
    int Number = 0;
    String restr;
    while (1) {
        xSemaphoreTake(command_list_samap, portMAX_DELAY);

        if (Serial2.available() != 0) {
            String str = Serial2.readString();
            restr += str;

            if (restr.indexOf("\r\n") != -1) {
            }

            if (restr.indexOf("+ZMMI:") != -1) {
                zmmi_str = restr;
            } else if ((restr.indexOf("OK") != -1) || (restr.indexOf("ERROR") != -1)) {
                Serial.print(restr);
                if (restr.indexOf("OK") != -1) {
                    if ((serial_at[0].command_type == kACTION_MO) || (serial_at[0].command_type == kASSIGN_MO)) {
                        serial_at.erase(serial_at.begin());
                        Serial.printf("erase now %d\n", serial_at.size());
                    } else {
                        serial_at[0].read_str = restr;
                        serial_at[0].state    = kWaitforRead;
                    }
                } else if (restr.indexOf("ERROR") != -1) {
                    serial_at[0].state = kErrorReError;
                } else {
                }
                restr.clear();
            }
        }

        if (serial_at.empty() != true) {
            Number = 0;
            switch (serial_at[0].state) {
                case kSendReady:
                    Serial.printf(serial_at[0].str_command.c_str());
                    Serial2.write(serial_at[0].str_command.c_str());
                    serial_at[0].state = kSending;
                    break;
                case kSending:

                    if (serial_at[0]._send_time_count > 0) {
                        serial_at[0]._send_time_count--;
                    } else {
                        serial_at[0].state = kWaitforMsg;
                    }
                    /* code */
                    break;
                case kWaitforMsg:
                    if (serial_at[0]._send_count > 0) {
                        serial_at[0]._send_count--;
                        serial_at[0]._send_time_count = serial_at[0].max_time;
                        Serial.printf(serial_at[0].str_command.c_str());
                        Serial2.write(serial_at[0].str_command.c_str());
                        restr.clear();
                        serial_at[0].state = 1;
                    } else {
                        serial_at[0].state = kErrorSendTimeOUT;
                    }
                    /* code */
                    break;
                case kWaitforRead:
                    /* code */
                    break;
                case 4:
                    /* code */
                    break;
                case kErrorSendTimeOUT:
                    /* code */
                    break;
                case 0xe2:
                    /* code */
                    break;
                default:
                    break;
            }
        }
        xSemaphoreGive(command_list_samap);
        delay(10);
    }
}

void AddMsg(String str, uint8_t type, uint16_t sendcount, uint16_t sendtime)
{
    struct ATCommand newcommand;
    newcommand.str_command      = str;
    newcommand.command_type     = type;
    newcommand.max_time         = sendtime;
    newcommand.send_max_number  = sendcount;
    newcommand.state            = 0;
    newcommand._send_count      = sendcount;
    newcommand._send_time_count = sendtime;
    xSemaphoreTake(command_list_samap, portMAX_DELAY);
    serial_at.push_back(newcommand);
    xSemaphoreGive(command_list_samap);
}

uint8_t readSendState(uint32_t number)
{
    xSemaphoreTake(command_list_samap, portMAX_DELAY);
    uint8_t restate = serial_at[number].state;
    xSemaphoreGive(command_list_samap);
    return restate;
}

uint32_t getATMsgSize()
{
    xSemaphoreTake(command_list_samap, portMAX_DELAY);
    uint32_t restate = serial_at.size();
    xSemaphoreGive(command_list_samap);
    return restate;
}
String ReadMsgstr(uint32_t number)
{
    xSemaphoreTake(command_list_samap, portMAX_DELAY);
    String restate = serial_at[number].read_str;
    xSemaphoreGive(command_list_samap);
    return restate;
}

bool EraseFirstMsg()
{
    xSemaphoreTake(command_list_samap, portMAX_DELAY);
    serial_at.erase(serial_at.begin());
    xSemaphoreGive(command_list_samap);
    return true;
}

uint16_t GetstrNumber(String Str, uint32_t* ptrbuff)
{
    uint16_t count = 0;
    String Numberstr;
    int indexpos = 0;
    while (Str.length() > 0) {
        indexpos = Str.indexOf(",");
        if (indexpos != -1) {
            Numberstr      = Str.substring(0, Str.indexOf(","));
            Str            = Str.substring(Str.indexOf(",") + 1, Str.length());
            ptrbuff[count] = Numberstr.toInt();
            count++;
        } else {
            ptrbuff[count] = Str.toInt();
            count++;
            break;
        }
    }
    return count;
}
vector<String> restr_v;
uint16_t GetstrNumber(String StartStr, String EndStr, String Str)
{
    uint16_t count = 0;
    String Numberstr;
    int indexpos = 0;

    Str = Str.substring(Str.indexOf(StartStr) + StartStr.length(), Str.indexOf(EndStr));
    Str.trim();
    restr_v.clear();

    while (Str.length() > 0) {
        indexpos = Str.indexOf(",");
        if (indexpos != -1) {
            Numberstr = Str.substring(0, Str.indexOf(","));
            Str       = Str.substring(Str.indexOf(",") + 1, Str.length());
            restr_v.push_back(Numberstr);
            count++;
        } else {
            restr_v.push_back(Numberstr);
            ;
            count++;
            break;
        }
    }
    return count;
}

String getReString(uint16_t Number)
{
    if (restr_v.empty()) {
        return String("");
    }
    return restr_v.at(Number);
}

uint16_t GetstrNumber(String StartStr, String EndStr, String Str, uint32_t* ptrbuff)
{
    uint16_t count = 0;
    String Numberstr;
    int indexpos = 0;

    Str = Str.substring(Str.indexOf(StartStr) + StartStr.length(), Str.indexOf(EndStr));
    Str.trim();

    while (Str.length() > 0) {
        indexpos = Str.indexOf(",");
        if (indexpos != -1) {
            Numberstr      = Str.substring(0, Str.indexOf(","));
            Str            = Str.substring(Str.indexOf(",") + 1, Str.length());
            ptrbuff[count] = Numberstr.toInt();
            count++;
        } else {
            ptrbuff[count] = Str.toInt();
            count++;
            break;
        }
    }
    return count;
}
uint32_t numberbuff[128];
String readstr;
uint8_t restate;

void setup()
{
    // put your setup code here, to run once:
    M5.begin();
    Serial2.begin(115200, SERIAL_8N1, 5, 13);

    Disbuff.createSprite(320, 20);
    Disbuff.fillRect(0, 0, 320, 20, BLACK);
    Disbuff.drawRect(0, 0, 320, 20, Disbuff.color565(36, 36, 36));
    Disbuff.pushSprite(0, 0);

    TerminalBuff.createSprite(120, 220);
    TerminalBuff.fillRect(0, 0, 120, 220, BLACK);
    TerminalBuff.drawRect(0, 0, 120, 220, Disbuff.color565(36, 36, 36));
    TerminalBuff.pushSprite(0, 20);
    terminal.setFontsize(2);
    terminal.setGeometry(0, 20, 120, 220);

    pinMode(2, OUTPUT);
    digitalWrite(2, 0);

    Disbuff.setTextColor(WHITE);
    Disbuff.setTextSize(1);
    for (int i = 0; i < 100; i++) {
        Disbuff.fillRect(0, 0, 320, 20, Disbuff.color565(36, 36, 36));
        Disbuff.pushSprite(0, 0);
        Disbuff.setCursor(7, 7);
        Disbuff.printf("Reset Module %02d", i);
        Disbuff.pushSprite(0, 0);
        delay(10);
    }
    digitalWrite(2, 1);

    xTaskCreate(LTEModuleTask, "LTEModuleTask", 1024 * 2, (void*)0, 4, &xhandle_lte_event);
    command_list_samap = xSemaphoreCreateMutex();
    xSemaphoreGive(command_list_samap);
}

void loop()
{
    AddMsg("AT+CSQ\r\n", kQUERY_MT, 1000, 1000);
    while ((readSendState(0) == kSendReady) || (readSendState(0) == kSending) || (readSendState(0) == kWaitforMsg))
        delay(50);
    restate = readSendState(0);
    readstr = ReadMsgstr(0).c_str();

    if ((readstr.indexOf("+CSQ:") != -1) && (restate == kWaitforRead)) {
        int count = GetstrNumber("+CSQ:", "OK", readstr, numberbuff);
        if (count != 0) {
            M5.Lcd.drawString("RSSI", 240, 60);
            // printf("CSQ-%d:%d\r\n",numberbuff[0],numberbuff[1]);
            if ((numberbuff[0] >= 2) && (numberbuff[0] <= 31) && (numberbuff[1] != 99)) {
                int rssi = 110 - ((numberbuff[0] - 2) * 2);
                terminal.printf("RSSI:-%d\r\n", rssi);
                M5.Lcd.fillRect(200, 50, 30, 30, GREEN);
            } else {
                M5.Lcd.fillRect(200, 50, 30, 30, RED);
            }
        }
    }
    EraseFirstMsg();

    Disbuff.print(readstr);
    terminal.print(readstr);

    AddMsg("AT+CREG?\r\n", kQUERY_MT, 1000, 1000);
    while ((readSendState(0) == kSendReady) || (readSendState(0) == kSending) || (readSendState(0) == kWaitforMsg))
        delay(50);
    restate = readSendState(0);
    readstr = ReadMsgstr(0).c_str();
    EraseFirstMsg();

    terminal.print(readstr);

    AddMsg("AT+COPS?\r\n", kQUERY_MT, 1000, 1000);
    while ((readSendState(0) == kSendReady) || (readSendState(0) == kSending) || (readSendState(0) == kWaitforMsg))
        delay(50);
    restate = readSendState(0);
    readstr = ReadMsgstr(0).c_str();
    if ((readstr.indexOf("+COPS:") != -1) && (restate == kWaitforRead)) {
        int count = GetstrNumber("+COPS:", "OK", readstr, numberbuff);
        if (count != 0) {
            M5.Lcd.drawString("Network", 240, 120);
            if (numberbuff[3] == 9) {
                M5.Lcd.fillRect(200, 110, 30, 30, GREEN);
            } else {
                M5.Lcd.fillRect(200, 110, 30, 30, RED);
            }
        }
    }
    EraseFirstMsg();

    Disbuff.print(readstr);
    terminal.print(readstr);

    delay(500);
    M5.update();
}
